package top.juc.plugin.idea.openfeignx.util;

import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleUtil;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiMethod;
import org.jetbrains.annotations.NotNull;
import top.juc.plugin.idea.openfeignx.annotation.Annotation;
import top.juc.plugin.idea.openfeignx.dto.FindClassDTO;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * The type Java utils.
 *
 * @author yanglin
 */
public final class CacheUtils {

    private CacheUtils() {
        throw new UnsupportedOperationException();
    }

    /**
     * Map<projectName, Map<className, List<fullPath>>
     */
    private static final ConcurrentHashMap<String, ConcurrentHashMap<String, List<String>>> CLASS_CACHE_MAP = new ConcurrentHashMap<>();

    /**
     * method注解缓存信息
     * <p>
     * Map<projectName+annotationName, Map<fullPath, PsiMethod>>
     */
    private static final ConcurrentHashMap<String, ConcurrentHashMap<String, PsiMethod>> ANNOTATION_CACHE_MAP = new ConcurrentHashMap<>();


    /**
     * 模块对应serviceNameMap
     * Map<moduleName, springApplicationName>
     */
    private static final ConcurrentHashMap<Module, String> MODULE_SERVICE_NAME_MAP = new ConcurrentHashMap<>();


    public static void init(@NotNull Project project, @NotNull List<Annotation> annotations) {
        synchronized (project) {
            FindClassDTO result = ModuleUtils.findClassByAnnotation(project, annotations);

            ConcurrentHashMap<Module, String> moduleStringMap = ConfigParseUtils.parseConfig(result.getModuleConfigMap());
            if(!moduleStringMap.isEmpty()){
                MODULE_SERVICE_NAME_MAP.putAll(moduleStringMap);
            }

            for (Annotation annotation : annotations) {
                ConcurrentHashMap<String, PsiMethod> methodAnnotationMap = getProjectMap(project, annotation);
                List<PsiClass> clazzList = result.getAnnotationClassMap().get(annotation);
                ConcurrentHashMap<String, PsiMethod> feignPathMap = null;
                if (Annotation.FEIGN_CLIENT.equals(annotation)) {
                    feignPathMap = SpringMvcUtils.getFeignPath(clazzList);
                } else if (Annotation.REST_CONTROLLER.equals(annotation)) {
                    feignPathMap = SpringMvcUtils.getRestControllerPath(clazzList);
                } else {
                    feignPathMap = new ConcurrentHashMap<>(0);
                }
                feignPathMap.forEach((fullPath, psiMethod) -> getClassList(project, psiMethod.getContainingClass()).add(fullPath));
                methodAnnotationMap.putAll(feignPathMap);
            }
        }
    }


    public static PsiMethod getMethod(@NotNull Project project, @NotNull Annotation annotation, String fullPathKey) {
        return getProjectMap(project, annotation).get(fullPathKey);
    }

    public static void setMethod(@NotNull Project project, @NotNull Annotation annotation, String fullPathKey, PsiMethod psiMethod) {
        getProjectMap(project, annotation).put(fullPathKey, psiMethod);
        getClassList(project, psiMethod.getContainingClass()).add(fullPathKey);
    }

    public static void clearProject(@NotNull Project project, @NotNull Annotation annotation) {
        ANNOTATION_CACHE_MAP.put(joinAnnotationMapKey(project, annotation), new ConcurrentHashMap<>());
        CLASS_CACHE_MAP.put(joinClassMapKey(project), new ConcurrentHashMap<>());
    }

    public static void clearClass(@NotNull Project project, @NotNull Annotation annotation, @NotNull PsiClass psiClass) {
        getClassList(project, psiClass).forEach(fullPath -> getProjectMap(project, annotation).remove(fullPath));
        CLASS_CACHE_MAP.get(joinClassMapKey(project)).remove(psiClass.getQualifiedName());
    }


    private static ConcurrentHashMap<String, PsiMethod> getProjectMap(@NotNull Project project, @NotNull Annotation annotation) {
        ConcurrentHashMap<String, PsiMethod> projectAnnotationMap = ANNOTATION_CACHE_MAP.get(joinAnnotationMapKey(project, annotation));
        if (Objects.isNull(projectAnnotationMap)) {
            projectAnnotationMap = new ConcurrentHashMap<>();
            ANNOTATION_CACHE_MAP.put(joinAnnotationMapKey(project, annotation), projectAnnotationMap);
        }
        return projectAnnotationMap;
    }

    private static String joinAnnotationMapKey(@NotNull Project project, @NotNull Annotation annotation) {
        return project.getName() + "_" + annotation.getQualifiedName();
    }

    private static String joinClassMapKey(@NotNull Project project) {
        return project.getName();
    }


    private static List<String> getClassList(@NotNull Project project, @NotNull PsiClass psiClass) {
        ConcurrentHashMap<String, List<String>> projectClassMap = CLASS_CACHE_MAP.get(joinClassMapKey(project));
        if (Objects.isNull(projectClassMap)) {
            projectClassMap = new ConcurrentHashMap<>();
            CLASS_CACHE_MAP.put(joinClassMapKey(project), projectClassMap);
        }
        List<String> fullPathList = projectClassMap.get(psiClass.getQualifiedName());
        if (Objects.isNull(fullPathList)) {
            fullPathList = new ArrayList<>();
            projectClassMap.put(psiClass.getQualifiedName(), fullPathList);
        }
        return fullPathList;
    }


    public static String getModuleSpringApplicationName(PsiClass psiClass) {
        Module module = ModuleUtil.findModuleForPsiElement(psiClass);
        if (module == null) {
            return "";
        }
        return MODULE_SERVICE_NAME_MAP.getOrDefault(module, "");
    }
}
